<script setup lang="tsx">
import { computed, reactive, ref, watch } from 'vue';
import type { CardSegmented } from 'naive-ui';
import { NButton } from 'naive-ui';
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
import { $t } from '@/locales';
import type { XxlJobInfo, XxlJobInfoEditAndCreateParams } from '@/service/api';
import { createXxlJobInfo, fetchXxlJobGroupOptions, updateXxlJobInfo } from '@/service/api';
import { useBoolean } from '~/packages/hooks/src';
import CronModel from '@/components/custom/cron-tab/index.vue';

defineOptions({
  name: 'JobInfoOperateModel'
});

interface Props {
  /** the type of operation */
  operateType: NaiveUI.TableOperateType | 'copy';
  /** the edit row data */
  rowData?: XxlJobInfo | null;
}

const props = defineProps<Props>();

interface Emits {
  (e: 'submitted'): void;
}

const emit = defineEmits<Emits>();

const visible = defineModel<boolean>('visible', {
  default: false
});
const { bool: cronModelVisible, setTrue: openCronModel } = useBoolean();

const { formRef, validate, restoreValidation } = useNaiveForm();
const { defaultRequiredRule } = useFormRules();

const submitLoading = ref(false);

const title = computed(() => {
  const titles: Record<NaiveUI.TableOperateType | 'copy', string> = {
    add: $t('page.base.xxl.job.addJob'),
    edit: $t('page.base.xxl.job.editJob'),
    copy: $t('page.base.xxl.job.addJob')
  };
  return titles[props.operateType];
});

const cardSegmented: CardSegmented = {
  content: true,
  footer: 'soft'
};

type Model = XxlJobInfoEditAndCreateParams;

const model: Model = reactive(createDefaultModel());

function createDefaultModel(): Model {
  return {
    id: 0,
    jobGroup: null,
    jobDesc: '',
    author: '',
    alarmEmail: '',
    scheduleType: 'CRON',
    scheduleConf: '',
    misfireStrategy: 'DO_NOTHING',
    executorRouteStrategy: 'FIRST',
    executorHandler: '',
    executorParam: '',
    executorBlockStrategy: 'SERIAL_EXECUTION',
    executorTimeout: null,
    executorFailRetryCount: null,
    glueType: 'BEAN',
    glueSource: '',
    glueRemark: `${$t('page.base.xxl.job.form.glueRemarkDefaultValue')}`,
    glueUpdatetime: '',
    childJobId: ''
  };
}

type RuleKey = Extract<
  keyof Model,
  'jobGroup' | 'jobDesc' | 'author' | 'scheduleType' | 'scheduleConf' | 'glueType' | 'executorHandler'
>;

const rules: Record<RuleKey, App.Global.FormRule> = {
  jobGroup: defaultRequiredRule,
  jobDesc: defaultRequiredRule,
  author: defaultRequiredRule,
  scheduleType: defaultRequiredRule,
  scheduleConf: defaultRequiredRule,
  glueType: defaultRequiredRule,
  executorHandler: defaultRequiredRule
};

const expression = ref('');

function handleInitModel() {
  Object.assign(model, createDefaultModel());

  if ((props.operateType === 'edit' || props.operateType === 'copy') && props.rowData) {
    Object.assign(model, props.rowData);
  }
}

function closeDrawer() {
  submitLoading.value = false;
  visible.value = false;
}

watch(visible, () => {
  if (visible.value) {
    handleInitModel();
    restoreValidation();
  }
});
watch(
  () => model.scheduleType,
  () => {
    if (model.scheduleType !== 'CRON') {
      model.scheduleConf = '';
    }
  }
);

const jobGroupOptions = ref<CommonType.Option<number>[]>([]);
async function getJobGroupOptions() {
  const { error, data } = await fetchXxlJobGroupOptions();

  if (!error) {
    const options = data.map(item => ({
      label: item.label,
      value: item.value
    }));

    jobGroupOptions.value = [...options];
  }
}

const scheduleTypeOptions: CommonType.Option[] = [
  {
    label: $t('page.base.xxl.job.scheduleTypeNone'),
    value: 'NONE'
  },
  {
    label: $t('page.base.xxl.job.scheduleTypeCron'),
    value: 'CRON'
  },
  {
    label: $t('page.base.xxl.job.scheduleTypeFixRate'),
    value: 'FIX_RATE'
  }
];

const executorModeOptions: CommonType.Option[] = [
  {
    label: 'BEAN',
    value: 'BEAN'
  },
  {
    label: 'GLUE(Java)',
    value: 'GLUE_GROOVY'
  },
  {
    label: 'GLUE(Shell)',
    value: 'GLUE_SHELL'
  },
  {
    label: 'GLUE(Python)',
    value: 'GLUE_PYTHON'
  },
  {
    label: 'GLUE(PHP)',
    value: 'GLUE_PHP'
  },
  {
    label: 'GLUE(Nodejs)',
    value: 'GLUE_NODEJS'
  },
  {
    label: 'GLUE(PowerShell)',
    value: 'GLUE_POWERSHELL'
  }
];

const executorRouteStrategyOptions: CommonType.Option[] = [
  {
    label: $t('page.base.xxl.job.jobconfRouteFirst'),
    value: 'FIRST'
  },
  {
    label: $t('page.base.xxl.job.jobconfRouteLast'),
    value: 'LAST'
  },
  {
    label: $t('page.base.xxl.job.jobconfRouteRound'),
    value: 'ROUND'
  },
  {
    label: $t('page.base.xxl.job.jobconfRouteRandom'),
    value: 'RANDOM'
  },
  {
    label: $t('page.base.xxl.job.jobconfRouteConsistentHash'),
    value: 'CONSISTENT_HASH'
  },
  {
    label: $t('page.base.xxl.job.jobconfRouteLfu'),
    value: 'LEAST_FREQUENTLY_USED'
  },
  {
    label: $t('page.base.xxl.job.jobconfRouteLru'),
    value: 'LEAST_RECENTLY_USED'
  },
  {
    label: $t('page.base.xxl.job.jobconfRouteFailover'),
    value: 'FAILOVER'
  },
  {
    label: $t('page.base.xxl.job.jobconfRouteBusyover'),
    value: 'BUSYOVER'
  },
  {
    label: $t('page.base.xxl.job.jobconfRouteShard'),
    value: 'SHARDING_BROADCAST'
  }
];

const misfireStrategyOptions: CommonType.Option[] = [
  {
    label: $t('page.base.xxl.job.misfireStrategyDoNothing'),
    value: 'DO_NOTHING'
  },
  {
    label: $t('page.base.xxl.job.misfireStrategyFireOnceNow'),
    value: 'FIRE_ONCE_NOW'
  }
];

const executorBlockStrategyOptions: CommonType.Option[] = [
  {
    label: $t('page.base.xxl.job.blockStrategySerialExecutor'),
    value: 'SERIAL_EXECUTION'
  },
  {
    label: $t('page.base.xxl.job.blockStrategyDiscardLater'),
    value: 'DISCARD_LATER'
  },
  {
    label: $t('page.base.xxl.job.blockStrategyCoverEarly'),
    value: 'COVER_EARLY'
  }
];

function handleShowCron() {
  expression.value = model.scheduleConf as string;
  openCronModel();
}
/** 确定后回传值 */
function crontabFill(value: string = '') {
  model.scheduleConf = value;
}

/** 提交 */
async function handleSubmit() {
  await validate();

  submitLoading.value = true;

  if (props.operateType === 'edit') {
    const { error } = await updateXxlJobInfo(model.id as number, model);

    if (!error) {
      window.$message?.success($t('common.updateSuccess'));
      closeDrawer();
      emit('submitted');
    }
  } else {
    const { error } = await createXxlJobInfo(model);
    if (!error) {
      window.$message?.success($t('common.addSuccess'));
      closeDrawer();
      emit('submitted');
    }
  }
}

getJobGroupOptions();
</script>

<template>
  <NModal v-model:show="visible" :title="title" preset="card" class="w-250">
    <NScrollbar class="max-h-200 pr-20px">
      <NForm ref="formRef" :model="model" :rules="rules" label-placement="left" label-width="120px">
        <!-- 基础配置 -->
        <NCard
          :segmented="cardSegmented"
          :bordered="false"
          footer-style="padding-bottom: 0;"
          content-style="padding-bottom: 0;"
        >
          <div class="text-gray-400">{{ $t('page.base.xxl.job.basicConfiguration') }}</div>
          <template #footer>
            <NGrid :x-gap="20" responsive="screen" item-responsive>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.jobGroup')"
                path="jobGroup"
                label-style="font-weight: 700;"
              >
                <NSelect
                  v-model:value="model.jobGroup"
                  :placeholder="$t('page.base.xxl.job.form.jobGroup')"
                  :options="jobGroupOptions"
                />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.jobDesc')"
                path="jobDesc"
                label-style="font-weight: 700;"
              >
                <NInput v-model:value="model.jobDesc" :placeholder="$t('page.base.xxl.job.form.jobDesc')" />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.author')"
                path="author"
                label-style="font-weight: 700;"
              >
                <NInput v-model:value="model.author" :placeholder="$t('page.base.xxl.job.form.author')" />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.alarmEmail')"
                path="alarmEmail"
                label-style="font-weight: 700;"
              >
                <NInput v-model:value="model.alarmEmail" :placeholder="$t('page.base.xxl.job.form.alarmEmail')" />
              </NFormItemGi>
            </NGrid>
          </template>
        </NCard>
        <!-- 调度配置 -->
        <NCard
          :segmented="cardSegmented"
          :bordered="false"
          footer-style="padding-bottom: 0;"
          content-style="padding-bottom: 0;"
        >
          <div class="text-gray-400">{{ $t('page.base.xxl.job.schedulingConfiguration') }}</div>
          <template #footer>
            <NGrid :x-gap="20" responsive="screen" item-responsive>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.scheduleType')"
                path="scheduleType"
                label-style="font-weight: 700;"
              >
                <NSelect
                  v-model:value="model.scheduleType"
                  :placeholder="$t('page.base.xxl.job.form.scheduleType')"
                  :options="scheduleTypeOptions"
                />
              </NFormItemGi>
              <NFormItemGi
                v-if="model.scheduleType === 'CRON'"
                span="24 m:12"
                :label="$t('page.base.xxl.job.scheduleConf')"
                path="scheduleConf"
                label-style="font-weight: 700;"
              >
                <NInputGroup>
                  <NInput v-model:value="model.scheduleConf" :placeholder="$t('page.base.xxl.job.form.scheduleConf')" />
                  <NInputGroupLabel class="cursor-pointer" @click="handleShowCron">
                    <div class="text-gray-500">
                      {{ $t('page.base.xxl.job.generatedExpression') }}
                      <icon-ant-design:edit-outlined />
                    </div>
                  </NInputGroupLabel>
                </NInputGroup>
              </NFormItemGi>
              <NFormItemGi
                v-if="model.scheduleType === 'FIX_RATE'"
                span="24 m:12"
                :label="$t('page.base.xxl.job.scheduleTypeFixRate')"
                path="scheduleConf"
                label-style="font-weight: 700;"
              >
                <NInput
                  v-model:value="model.scheduleConf"
                  :placeholder="$t('page.base.xxl.job.form.scheduleTypeFixRate')"
                />
              </NFormItemGi>
            </NGrid>
          </template>
        </NCard>
        <!-- 任务配置 -->
        <NCard
          :segmented="cardSegmented"
          :bordered="false"
          footer-style="padding-bottom: 0;"
          content-style="padding-bottom: 0;"
        >
          <div class="text-gray-400">{{ $t('page.base.xxl.job.jobConfiguration') }}</div>
          <template #footer>
            <NGrid :x-gap="20" responsive="screen" item-responsive>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.executorMode')"
                path="glueType"
                label-style="font-weight: 700;"
              >
                <NSelect
                  v-model:value="model.glueType"
                  :disabled="operateType === 'edit'"
                  :placeholder="$t('page.base.xxl.job.form.executorMode')"
                  :options="executorModeOptions"
                />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.executorHandler')"
                path="executorHandler"
                label-style="font-weight: 700;"
              >
                <NInput
                  v-model:value="model.executorHandler"
                  :disabled="model.glueType !== 'BEAN'"
                  clearable
                  :placeholder="$t('page.base.xxl.job.form.executorHandler')"
                />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:24"
                :label="$t('page.base.xxl.job.executorParam')"
                path="executorParam"
                label-style="font-weight: 700;"
              >
                <NInput
                  v-model:value="model.executorParam"
                  type="textarea"
                  :placeholder="$t('page.base.xxl.job.form.executorParam')"
                />
              </NFormItemGi>
            </NGrid>
          </template>
        </NCard>
        <!-- 高级配置 -->
        <NCard
          :segmented="cardSegmented"
          :bordered="false"
          footer-style="padding-bottom: 0;"
          content-style="padding-bottom: 0;"
        >
          <div class="text-gray-400">{{ $t('page.base.xxl.job.advancedConfiguration') }}</div>
          <template #footer>
            <NGrid :x-gap="20" responsive="screen" item-responsive>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.executorRouteStrategy')"
                path="executorRouteStrategy"
                label-style="font-weight: 700;"
              >
                <NSelect
                  v-model:value="model.executorRouteStrategy"
                  :placeholder="$t('page.base.xxl.job.form.executorRouteStrategy')"
                  :options="executorRouteStrategyOptions"
                />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.childJobId')"
                path="childJobId"
                label-style="font-weight: 700;"
              >
                <NInput v-model:value="model.childJobId" :placeholder="$t('page.base.xxl.job.form.childJobId')" />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.misfireStrategy')"
                path="misfireStrategy"
                label-style="font-weight: 700;"
              >
                <NSelect
                  v-model:value="model.misfireStrategy"
                  :placeholder="$t('page.base.xxl.job.form.misfireStrategy')"
                  :options="misfireStrategyOptions"
                />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.executorBlockStrategy')"
                path="executorBlockStrategy"
                label-style="font-weight: 700;"
              >
                <NSelect
                  v-model:value="model.executorBlockStrategy"
                  :placeholder="$t('page.base.xxl.job.form.executorBlockStrategy')"
                  :options="executorBlockStrategyOptions"
                />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.executorTimeout')"
                path="executorTimeout"
                label-style="font-weight: 700;"
              >
                <NInputNumber
                  v-model:value="model.executorTimeout"
                  :placeholder="$t('page.base.xxl.job.form.executorTimeout')"
                  class="w-full"
                />
              </NFormItemGi>
              <NFormItemGi
                span="24 m:12"
                :label="$t('page.base.xxl.job.executorFailRetryCount')"
                path="executorFailRetryCount"
                label-style="font-weight: 700;"
              >
                <NInputNumber
                  v-model:value="model.executorFailRetryCount"
                  :placeholder="$t('page.base.xxl.job.form.executorFailRetryCount')"
                  class="w-full"
                />
              </NFormItemGi>
            </NGrid>
          </template>
        </NCard>
      </NForm>
      <!-- Cron弹窗 -->
      <CronModel v-model:visible="cronModelVisible" :expression="expression" @fill="crontabFill" />
    </NScrollbar>
    <template #footer>
      <NSpace justify="end" :size="16">
        <NButton @click="closeDrawer">{{ $t('common.cancel') }}</NButton>
        <NButton type="primary" :loading="submitLoading" @click="handleSubmit">{{ $t('common.confirm') }}</NButton>
      </NSpace>
    </template>
  </NModal>
</template>

<style scoped></style>
